package org.wattdepot.cli;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.client.WattDepotClientException;
import org.wattdepot.resource.property.jaxb.Property;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.resource.source.jaxb.SubSources;

/**
 * Processes the fueltypes command.
 * 
 * @author Dean Kim
 * 
 */
public class FuelTypesCommandCli {

  private Map<String, List<String>> fuelTypeMap;
  // WattDepotClient client;
  private static final String FUELTYPES_NOT_FOUND = "No fueltypes were found.\n";
  private static final String INVALID_SOURCE = "Invalid source.\n";
  protected static final String FUELTYPES_COMMAND_ERROR = "Invalid fueltypes command. Type "
      + "help for usage.\n";

  /**
   * Initializes instance.
   */
  public FuelTypesCommandCli() {
    super();

    this.fuelTypeMap = new HashMap<String, List<String>>();
  }

  /**
   * Poputes multimap with fueltypes and source association.
   * 
   * @param propertyList Valid source property list.
   * @param source valid source.
   */
  protected void populateFuelTypeMap(List<Property> propertyList, Source source) {
    
    String fuelType;
    
    for (Property p : propertyList) {
      if (p.getKey().equals("fuelType")) {
        fuelType = p.getValue();
        List<String> fuelValue = this.fuelTypeMap.get(fuelType);
        if (fuelValue == null) {
          this.fuelTypeMap.put(fuelType, fuelValue = new ArrayList<String>());
        } // end if (fuelValue == null)
        fuelValue.add(source.getName());
      } // end if (p.getKey().equals("fuelType"))
    } // end for (Property p : propertyList)
    
  }
  
  
  /**
   * Interface for populating multimap storing fueltypes and sources with fueltypes 
   * for the given source.
   * 
   * @param source Valid source.
   */
  protected void processFuelTypeMap(Source source) {

    List<Property> propertyList;

    if (source.isSetProperties()) {
      propertyList = source.getProperties().getProperty();
      this.populateFuelTypeMap(propertyList, source);
    } // end if (source.isSetProperties())
  }

  /**
   * Returns subsources for a given source.
   * 
   * @param client Valid WattDepotClient.
   * @param source Valid Source.
   * @return All subsources found for the given source.
   */
  protected String[] getSubsources(WattDepotClient client, Source source) {

    String[] subsources = null;
    SubSources ss;

    ss = source.getSubSources();
    if (ss != null) {
      subsources = ss.toString().split(",");
      for (int i = 0; i < subsources.length; i++) {
        subsources[i] = this.filterHrefString(subsources[i]);
      }

    }

    return subsources;
  }

  /**
   * Gets Formatted return of fueltypes for a single source.
   * 
   * @param source Valid source.
   * @return Formatted return of fueltypes for a source.
   */
  protected String getSingleSourceFuelType(Source source) {

    String result = "";
    List<Property> propertyList;
    String fuelType;
    StringBuffer sb = new StringBuffer(50);

    if (source.isSetProperties()) {
      propertyList = source.getProperties().getProperty();
      for (Property p : propertyList) {
        if (p.getKey().equals("fuelType")) {
          fuelType = p.getValue();
          sb.append(fuelType + " : " + source.getName() + "\n");
        }
      }
    }
    else {
      result = FuelTypesCommandCli.FUELTYPES_NOT_FOUND;
    }
    result = sb.toString();

    return result;

  }

  /**
   * Processes virtual clients to return fueltypes for all subsources.
   * 
   * @param client Valid WattDepotClient.
   * @param source valid source.
   * @throws WattDepotClientException Throws WattDepotClientException
   */
  protected void processSourceRecursive(WattDepotClient client, Source source)
      throws WattDepotClientException {

    String[] subsources;

    subsources = this.getSubsources(client, source);

    if (subsources != null) {
      for (int i = 0; i < subsources.length; i++) {
        this.processSourceRecursive(client, client.getSource(subsources[i]));
      }

    }
    this.processFuelTypeMap(source);
  }

  /**
   * Gets the formatted string output of fueltypes and sources mapping.
   * 
   * @return Formatted string output of fueltypes and sources mapping.
   */
  protected String getFuelTypesFromMap() {

    Set<String> fuelTypes;
    fuelTypes = this.fuelTypeMap.keySet();
    StringBuffer sb = new StringBuffer();

    for (String ft : fuelTypes) {
      sb.append(ft);
      sb.append(" : ");
      for (String src : this.fuelTypeMap.get(ft)) {
        sb.append(src.toString());
        sb.append(',');
      }
      sb = new StringBuffer(sb.substring(0, (sb.length() - 1)));
      sb.append('\n');
    }

    return (sb.toString());

  }

  /**
   * Utility method to parse and strip href strsing to remove URI.
   * 
   * @param href Href string.
   * @return String value minus the URI.
   */
  protected String filterHrefString(String href) {

    String[] tokenHref;
    String filteredResult;

    tokenHref = href.split("/");
    filteredResult = tokenHref[5].replaceAll("]", "");

    return filteredResult;

  }

  /**
   * Processes virtual sources or sources with subsources to extract fueltypes.
   * @param client Valid WattDepot client.
   * @param source Valid source.
   * @return Formatted return of fueltypes mapping to sources. 
   * @throws WattDepotClientException Throws WattDepotClient
      if (p.getKey().equals("fuelType")) {Exception.
   */
  protected String processVirtualSource(WattDepotClient client, Source source)
      throws WattDepotClientException {

    try {
      this.processSourceRecursive(client, source);
    }
    catch (WattDepotClientException e) {
      // TODO Auto-generated catch block
      throw e;
    }

    return (this.getFuelTypesFromMap());

  }

  /**
   * Entry and return point for processing list source and list sources commands.
   * 
   * @param client Valid WattDepotClient.
   * @param inputString Tokenized command.
   * @return Formatted output for the fueltypes commands.
   * @throws WattDepotClientException Throws WattDepotClientException.
   */
  protected String processCommand(WattDepotClient client, String[] inputString)
      throws WattDepotClientException {

    Source source;
    String result = "";
    String[] subsource;

    if (inputString.length == 2) {

      try {
        source = client.getSource(inputString[1]);
        subsource = this.getSubsources(client, source);
        if (subsource == null) {
          result = this.getSingleSourceFuelType(source);
        }
        else {
          result = this.processVirtualSource(client, source);
        }
      }
      catch (ResourceNotFoundException e) {
        result = FuelTypesCommandCli.INVALID_SOURCE;
      }
    } // end if (sourceString.length == 2)
    else {
      result = FuelTypesCommandCli.FUELTYPES_COMMAND_ERROR;
    }

    return result;
  }
}
